Kafka의 Consumer Thread 최적화하기
메모리 제약 조건 하에서 Kafka의 처리 성능을 개선한 경험을 공유하고자 한다.
기존 상황과 문제점
처음에는 단일 컨슈머 스레드를 사용했다. 이 구성으로는 초당 약 400개의 메시지밖에 처리하지 못하는 한계가 있었다. 물론 사용자가 없는 프로젝트이긴 했지만, 사용자가 있다는 가정을 한다면 실시간 로그를 많은 사용자 애플리케이션에서 수집해야 하므로 성능 향상이 필요하다.
성능 개선을 위한 분석
가용 메모리가 1GB로 제한된 상황에서, 최적의 구성을 찾기 위해 몇 가지 계산을 진행했다.
메모리 사용량 분석
- 데이터 크기: 최악의 경우를 가정하여 10KB
- Consumer Thread 1개 추가 시: 약 60MB 메모리 증가
최적의 구성 도출
표: 3초 간격으로 총 소비된 메시지 수(thread 1개)
Timestamp | Total Messages Consumed | Messages Consumed in 3 Seconds |
---|---|---|
2024-05-23 08:05:45.247421 | 588 | - |
2024-05-23 08:05:48.247421 | 1347 | 759 |
2024-05-23 08:05:51.247421 | 2441 | 1094 |
2024-05-23 08:05:54.247421 | 3579 | 1138 |
2024-05-23 08:05:57.247421 | 4806 | 1227 |
2024-05-23 08:06:00.247421 | 5724 | 918 |
2024-05-23 08:06:03.247421 | 6604 | 880 |
2024-05-23 08:06:06.247421 | 7598 | 994 |
2024-05-23 08:06:09.247421 | 8789 | 1191 |
2024-05-23 08:06:12.247421 | 9823 | 1034 |
2024-05-23 08:06:15.247421 | 10983 | 1160 |
2024-05-23 08:06:18.247421 | 12221 | 1238 |
2024-05-23 08:06:21.247421 | 13491 | 1270 |
2024-05-23 08:06:24.247421 | 14770 | 1279 |
2024-05-23 08:06:27.247421 | 16017 | 1247 |
2024-05-23 08:06:30.247421 | 17329 | 1312 |
2024-05-23 08:06:33.247421 | 18586 | 1257 |
2024-05-23 08:06:36.247421 | 19883 | 1297 |
1초 평균 : 383.3
표: 3초 간격으로 총 소비된 메시지 수(thread 2개)
Timestamp | Total Messages Consumed | Messages Consumed in 3 Seconds |
---|---|---|
2024-05-23 08:03:23.481527 | 891 | - |
2024-05-23 08:03:26.481527 | 1773 | 882 |
2024-05-23 08:03:29.481527 | 2877 | 1104 |
2024-05-23 08:03:32.481527 | 4472 | 1595 |
2024-05-23 08:03:35.481527 | 6308 | 1836 |
2024-05-23 08:03:38.481527 | 8348 | 2040 |
2024-05-23 08:03:41.481527 | 9793 | 1445 |
2024-05-23 08:03:44.481527 | 11250 | 1457 |
2024-05-23 08:03:47.481527 | 12850 | 1600 |
2024-05-23 08:03:50.481527 | 14429 | 1579 |
2024-05-23 08:03:53.481527 | 16342 | 1913 |
2024-05-23 08:03:56.481527 | 18360 | 2018 |
2024-05-23 08:03:59.481527 | 20527 | 2167 |
1초 평균 : 576
표: 3초 간격으로 총 소비된 메시지 수(thread 5개)
Timestamp | Total Messages Consumed | Messages Consumed in 3 Seconds |
---|---|---|
2024-05-23 07:02:21.178118 | 2552 | - |
2024-05-23 07:02:24.178118 | 4750 | 2198 |
2024-05-23 07:02:27.178118 | 6981 | 2231 |
2024-05-23 07:02:30.178118 | 9880 | 2899 |
2024-05-23 07:02:33.178118 | 13096 | 3216 |
2024-05-23 07:02:36.178118 | 16409 | 3313 |
2024-05-23 07:02:39.178118 | 18138 | 1729 |
2024-05-23 07:02:42.178118 | 20233 | 2095 |
2024-05-23 07:02:45.178118 | 22698 | 2465 |
2024-05-23 07:02:48.178118 | 24582 | 1884 |
1초 평균 : 927
표: 3초 간격으로 총 소비된 메시지 수(thread 10개)
Timestamp | Total Messages Consumed | Messages Consumed in 3 Seconds |
---|---|---|
2024-05-23 07:02:21.178118 | 2867 | - |
2024-05-23 07:02:24.178118 | 6353 | 3486 |
2024-05-23 07:02:27.178118 | 9587 | 3234 |
2024-05-23 07:02:30.178118 | 12700 | 3113 |
2024-05-23 07:02:33.178118 | 15146 | 2446 |
2024-05-23 07:02:36.178118 | 17630 | 2484 |
2024-05-23 07:02:39.178118 | 20565 | 2935 |
2024-05-23 07:02:42.178118 | 23887 | 3322 |
2024-05-23 07:02:45.178118 | 27417 | 3530 |
2024-05-23 07:02:48.178118 | 30987 | 3570 |
2024-05-23 07:02:51.178118 | 34352 | 3365 |
2024-05-23 07:02:54.178118 | 37950 | 3598 |
2024-05-23 07:02:57.178118 | 41499 | 3549 |
2024-05-23 07:03:00.178118 | 45007 | 3508 |
2024-05-23 07:03:03.178118 | 48560 | 4553 |
2024-05-23 07:03:06.178118 | 52065 | 3705 |
2024-05-23 07:03:09.178118 | 55547 | 4482 |
2024-05-23 07:03:12.178118 | 59054 | 3507 |
1초 평균 : 1104
이 데이터를 분석해보면 스레드 개수 증가에 따른 처리량이 다음과 같다:
- 1개 스레드: 383.3 messages/sec
- 2개 스레드: 576 messages/sec (1.5배 증가)
- 5개 스레드: 927 messages/sec (2.4배 증가)
- 10개 스레드: 1104 messages/sec (2.9배 증가)
스레드 수를 늘릴 때마다 성능 향상폭이 감소하는 것을 볼 수 있다.
- 1→2개: 약 50% 성능 향상
- 2→5개: 약 61% 성능 향상
- 5→10개: 약 19% 성능 향상
특히 5개 -> 10개 구간은 성능 향상이 이전에 비해 굉장히 완만해진다.
5개 스레드와 10개 스레드를 구체적으로 비교해보자.
스레드 5개:
- 평균 초당 927개 처리
- 메모리 사용: 약 300MB (60MB × 5)
- 처리량이 안정적 (데이터의 편차가 상대적으로 작음)
스레드 10개:
- 평균 초당 1104개 처리
- 메모리 사용: 약 600MB (60MB × 10)
- 처리량의 변동폭이 큼 (2,446 ~ 4,553 범위로 변동)
따라서 10개 스레드는 5개 대비 메모리를 2배 사용하지만, 성능은 19% 정도만 향상되고, 처리량이 불안정하다. 그래서 5개의 스레드를 두기로 결정했다.
물론 사용량이 많아 5개의 스레드로 처리를 감당할 수 없다면 또 다른 고민이 필요할 것이다.
성능 지표(5개 스레드 기준)
- 처리량: 초당 약 927개 메시지 처리
- 전체 메모리 사용량: 352MB
- Thread 메모리: 300MB
- Buffer: 32MB
- Segment & Index: 20MB
352MB를 할당해 처리 성능을 약 2.4배 향상시킬 수 있었다.
실제 운영 환경에서는 항상 리소스 제약이 있는데, 여러 제약 조건 내에서 최적의 성능을 끌어내는 것이 중요하다고 생각한다.